/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package polybook.model;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import polybook.utils.Log;
import polybook.utils.ProcessCallBackable;
import polybook.utils.RandomUtils;

/**
 * Cette classe est le réseau, elle regroupe un ensemble de personnes ainsi que
 * les liens qui les unies.
 */
public class NetworkTabLinked extends Network {

    /**
     * Tableau contenant les personnes en fonction de leur nombre d'amis.
     */
    protected ArrayList<Person> tabPersonForLink;

    /**
     * Constructeur par défaut.
     */
    public NetworkTabLinked(ProcessCallBackable callbackObject) {
        this.container = new ArrayList<Person>();
        this.tabPersonForLink = new ArrayList<Person>();
        this.callbacker = callbackObject;
    }

    /**
     * Constructeur du Kernel, initialisation de la liste aléatoire de personne.
     * Création des liens d'amitiés entre les personnes lors de l'initialisation
     * en comparaison de la probabilité passée en paramètres.
     *
     * @param numberPeople Nombre de personnes à créer lors de l'initialisation
     * @param callbackObject Objet implementant l'interface ProcessCallBackable
     * permettant de reagir au changement d'etat du reseau du kernel.
     */
    public NetworkTabLinked(int numberPeople, ProcessCallBackable callbackObject) throws IOException, IllegalAccessException {
        this(callbackObject);

        for (int i = 0; i < numberPeople; i++) {
            // Creation d'une personne aleatoire avec pseudo unique = "person" + i;
            String pseudo = RandomUtils.generateString(5, 15, false) + i;
            Person person = new Person(pseudo, true, this);
            // Ajout de la personne au container
            this.container.add(person);
            // Creation des liens
            addLink(person);
            // Ajout dans le tableau des amis
            for (int j = 0; j < person.getFriendList().size(); j++) {
                this.tabPersonForLink.add(person);
            }
        };
    }

    public NetworkTabLinked(int numberPeople) throws IOException, IllegalAccessException {
        this(numberPeople, null);
    }

    /**
     * Cette méthode crée les liens d'amitiés entre les personnes du réseau lors
     * de l'initialisation. La probabilité aléatoire est comparée avec la
     * probabilité que l'on a passé en paramètre car lors de l'initialisation
     * aucune probabilité ne peut être calculée.
     *
     */
    @SuppressWarnings("empty-statement")
    protected void initNetwork() {

        for (Person onePerson : container) {;
            addLink(onePerson);
        }

    }

    /**
     * Cette méthode ajoute un nombre de personnes passé en paramètre au réseau
     * existant. Le réseau est complété et les liens d'amitiés ajoutés en
     * fonction de la probabilité de chaque personne du réseau.
     *
     * @param addNb - Nombre de personne à ajouter au réseau.
     */
    @Override
    public void addPersonAtNetwork(int addNb) throws IOException, IllegalAccessException {
        if (this.container.isEmpty()) {
            throw new IllegalAccessException("Attention le réseau doit être initialisé avant d'ajouter une personne");
        }
        // Mise en cache des taille
        int taille = this.container.size();
        // Initialisation des variables
        Random rdmObject = new Random();
        int currentTabIndex;
        Person currentSelectedPerson;


        // Boucle de creation de personne
        for (int i = taille; i <= addNb + taille; i++) {
            // Creation de la personne aleatoire
            String pseudo = RandomUtils.generateString(5, 15, false) + i;
            Person newPerson = new Person(pseudo, true, this);
            // Ajout du lien avec le tableau de liens
            for (int j = 0; j < this.container.size(); j++) {
                currentTabIndex = rdmObject.nextInt(this.tabPersonForLink.size());
                currentSelectedPerson = this.tabPersonForLink.get(currentTabIndex);
                // TODO : ici optimisation a prevoir : un "contains" va parcourir la liste d'amis...
                if (!newPerson.getFriendList().contains(currentSelectedPerson)) {
                    newPerson.linkTo(currentSelectedPerson);
                }

            }
            // Mise a jour du container et du tableau d'amis
            this.container.add(newPerson);
            for (int k = 0; k < newPerson.getFriendList().size(); k++) {
                this.tabPersonForLink.add(newPerson);
            }
            // Log.v("Creation personne " + pseudo + " / " + newPerson.getFriendList().size() + " amis");

            if (this.callbacker != null) {
                this.callbacker.callback((float) (i - taille) / (float) addNb);
            }
        }

        Log.v("FIN DES GENERATIONS");
        Log.v("Taille du reseau : " + this.container.size());
        Log.v("Taille du tableau d'amitie : " + this.tabPersonForLink.size());
        float probToFriend = (float) this.container.size() / this.tabPersonForLink.size();
        Log.v("Probabilite moyenne d'une personne d'avoir des amis : " + Float.toString(probToFriend));
    }

    /**
     * Cette méthode appel la méthode addLink() en envoyant le paramètre false
     * en plus, elle permet de ne pas ecrire deux méthodes identique mais de
     * faire un traitement différent dans le cas où nous sommes à
     * l'initialisation. Nous précisons la valeur du booléen uniquement lorsque
     * nous sommes dans l'initialisation.
     *
     * @param Person Personne que l'on va comparer à tous le réseau pour créer
     * des liens d'amitiés.
     *
     *
     * protected void addLink(Person onePerson2) { addLink(onePerson2); }
     *
     * /*
     * Cette méthode test la personne passée en paramètres avec toutes les
     * personnes du réseau. Elle crée des liens d'amitiés selon le pourcentage
     * calculé.
     *
     * @param Person La personne que l'on va comparer à tout le réseau. @param
     * Boolean Indiquant si l'on se trouve dans l'initialisation ou non.
     */
    @Override
    protected final void addLink(Person onePerson1) {
        // Parcour du reseau
        for (Person onePerson : this.container) {
            // On evite la personne courante
            if (!onePerson.pseudoPerson.equals(onePerson1.pseudoPerson)) {
                double probaAleatoire = Math.random();
                if (probaAleatoire <= NetworkTabLinked.INIT_PROBA) {
                    onePerson.friendListPerson.add(onePerson1);
                    onePerson1.friendListPerson.add(onePerson);
                    tabPersonForLink.add(onePerson);
                    tabPersonForLink.add(onePerson1);
                    Log.v("Size " + tabPersonForLink.size());
                    this.nbLink++;
                }
            }
        }
    }

    /**
     * Supprime les différents liens existants entre la personne passée en
     * paramètres et les autres membres du réseaux.
     *
     * @param String Le pseudo de la personne à détacher du réseaux
     */
    @Override
    public void deleteLink(Person personToDelete) throws IllegalArgumentException {
        if (!this.container.contains(personToDelete)) {
            throw new IllegalArgumentException("Aucune personne du réseau n'a ce pseudo");
        }
        for (Person friendPersonToDelete : personToDelete.friendListPerson) {
            // Pour chacun des amis de la personne à supprimer on la supprime de la liste de cet ami
            friendPersonToDelete.friendListPerson.remove(personToDelete);
            // Pour conserver un modèle stable, on supprime également les liens de la personne à détacher
            personToDelete.friendListPerson.remove(friendPersonToDelete);
            this.nbLink--;
        }
    }

    //Return le nombre de lien du reseau.
    @Override
    public int getNbLink() {
        return this.tabPersonForLink.size();
    }
}
